2016-04-12 54 views
7

ファイルをアップロードして読み込もうとしていますが、すべてがうまくいきますが、ハンドラメソッドに@Async注釈を付けるとうまくいきません。スプリング非同期ファイルのアップロードと処理

私は、ユーザーがファイルを処理するまで待つことを望んでいません。しかし、この注釈を入れた後、私はjava.lang.IllegalStateException: File has been moved - cannot be read again例外を得る。何が起こり、これをどうやって修正するのですか?私が理解しているように、要求応答が終了し、それをクリーンアップするので、Springは単にファイルをクリアすることができます。しかし、これを防ぐべきではない@Async

サンプル春ブートアプリケーション:

@SpringBootApplication 
@EnableSwagger2 
@ComponentScan(value = "hello") 
@EnableAsync 
public class Application { 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    public Docket api() { 
     return new Docket(DocumentationType.SWAGGER_2) 
       .select() 
       .apis(RequestHandlerSelectors.any()) 
       .paths(PathSelectors.regex("/api/.*")) 
       .build(); 
    } 
} 

アップロードコントローラ:

@RestController 
@RequestMapping(value = "/files") 
public class FilesController { 

    @Inject 
    private Upload upload; 

    @RequestMapping(method = RequestMethod.POST) 
    public void addSource(@RequestParam MultipartFile file) throws IOException, InterruptedException { 
     upload.process(file); 
    } 
} 

アップロードサービス:

@Component 
public class Upload { 

    @Async 
    public void process(MultipartFile file) throws InterruptedException, IOException { 
     sleep(2000); 
     System.out.println(new String(IOUtils.readFully(file.getInputStream(), -1, false))); 
    } 
} 

そして今、私はjava.io.FileNotFoundExceptionを取得します。私はここで何が欠けているのか分からない。おそらく、私はこれに何か誤りが見つからないので、何か間違っていると思います。これは非常に一般的なユースケースだと思います。

+0

ファイルをアップロードするときにポストリクエストを送信するのか、それともajaxリクエストを行いますか? –

+0

さらに、サービスメソッドまたはコントローラで@Asyncアノテーションを使用しましたか? –

+0

シンプルなPOSTリクエストです(スワッガー経由)。 @Asyncアノテーションをサービスメソッドに配置します。 – bargoras

答えて

10

あなたは@AsyncメソッドにMultipartFileパラメータを渡すことはできません。

"addSource"メソッドが終了すると、MultipartFileは有効範囲外になり、リソースが解放されます。したがって、 "プロセス"メソッド内のアクセスは失敗します。あなたはこのような競合状態を作ります。 スプリングDispatcherServletは、StandardServletMultipartResolver.cleanupMultipartを使用してこれらのファイルをクリーンアップします。このメソッドがいつaddSource(...)の呼び出し時に呼び出されるかを見るためにブレークポイントを設定します。

以下を実行する必要があります。ファイル全体を「addSource」メソッド内のバッファに読み込みます。次に、バッファを "process"メソッドに渡します。 "addSource"を返します。

"...ファイルを処理しています..."とは、ファイルを "処理中"ではなく読み込んでいます。

@Async 
public void process(byte[] bs){ 
    System.out.println(new String(bs)); 
    //do some long running processing of bs here 
} 

@RequestMapping(method = RequestMethod.POST) 
public void addSource(@RequestParam MultipartFile file) { 
    upload.process(IOUtils.toByteArray(file)); 
} 
+0

ありがとう! Btwここでは長い処理をしていますが、これは単なる例です。 – bargoras

+0

いい説明!しかし残念なことに、提案されている解決法には欠点があります。エンタープライズ環境では、そのようなメモリにバッファリングすることができないサイズのファイルがあることがよくあります。残念ながら、 'InputStreamResource'は動作しません。 あなたが今までに知る限り、処理スレッドの最後でクリーンアップすることができる独自の一時ファイル(例: 'File#createTempFile')にコンテンツをコピーするのが最善の策です。 – Powerslave

関連する問題